Fix data race when rendering an LCD display #507
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
The example code for the LCD display uses two threads: one for simulating the AVR chip, and one for OpenGL. The state of the LCD display is stored as some flags in a
uint16_t
variable. When the OpenGL thread modifies a flag, it may accidentally undo a simultaneous write operation in the AVR thread, causing malfunction of the display.In this PR I add mutex support to the LCD chip simulation. The pthread library is only included in the OpenGL-related parts of the code, so it is still possible to compile the LCD simulation itself without the threading library.
The introduction of this mutex does not noticeably affect performance.
The problem
The LCD display is stored in a struct
hd44780_t
. This struct has a 2 byte integer member which contains various bit flags. One flag is for the internal status: whether the chip is reading the first or second nibble of the current byte. The other flag id for rendering: whether the content of the LCD screen needs to be redrawn. Since the access to this variable is not guarded, the OpenGL thread, while clearing the redraw flag, can also overwrite the other flag if it is changed by the simulation thread at the same time. After this, the LCD chip will interpret the incoming nibbles in the wrong order.Reproduction: The first commit of this PR adds a new LCD fps testing program to the examples. Check out this commit, build the project, and execute
A counter starts counting on the LCD screen:
After some time (less than a minute on my laptop), garbage data starts to appear:
Solution
I added a new global mutex to the LCD OpenGL utilities. The both the drawing thread and the AVR thread uses this. The
hd44780_t
struct got two new callback members, one for locking and one for unlocking. This callback mechanism allows to use the struct without mutexes, if not needed. Finally, I split the flags of the LCD into two groups: there is a group of internal flags, which are not protected by the mutex (these flags are not needed for drawing), and there is a protected group. This splitting results in less mutex locking.I tested the performance of this solution with the fps testing program. Both the old and new code displayed about 260000 frames in 1:54 (for this test it is important to redirect the output to
/dev/null
, otherwise the simulation will slow down).Other possible ways to solve the problem
hd44780_t
struct: this would mean that either pthread is required for compiling this component, or that it is guarded by a preprocessor macro.